#include <LocalCamera.hpp>
#include <QAction>
#include <QKeyEvent>
#include <QColorDialog>
#undef ERROR
#include <glog/logging.h>

#include "BaseRenderWidget.hpp"

using rtrt::BaseRenderWidget;
using rtrt::BaseRender;

using Eigen::Vector3f;
using Eigen::AngleAxisf;

BaseRenderWidget::BaseRenderWidget( QWidget *parent ) : QGLWidget( parent             ), _camera( new LocalCamera( Eigen::Vector3f( 0.0, 0.0, 0.0 ))),
                                                       _timer    ( new QTimer( this ) ),
                                                       _fps      ( 0.0F               ), _frames( 0                       ), _refreshTime  ( 1000               ),
                                                       _workTime( new QTime() ), _fpsTime( new QTime()), _text( "Tahoma", 12, Qt::green ), _useStereo( false ),
                                                       _showFPS  ( true               ), _showCameraData( false           )
{
  
  setFormat( QGLFormat( QGL::DoubleBuffer | QGL::DepthBuffer ) );
  setAutoBufferSwap( false );
 
//   _camera->setFrustum ( );
//   _camera->setViewport ( width(), height() );
 
  _timer = new QTimer( this );
  _timer->start( 0 );
  
  connect( _timer, SIGNAL( timeout()), this, SLOT( updateWindow() ) ); 
  
  createActions();
  
}

void BaseRenderWidget::createActions()
{
  
  _useStereoModeAction = new QAction( tr( "Use stereo" ), this );
  _useStereoModeAction-> setCheckable( true  );
  _useStereoModeAction-> setChecked  ( false );
  
  connect( _useStereoModeAction, SIGNAL( toggled(bool) ), this, SLOT( useStereo(bool) ) );
  
  _useOpenCLModeAction = new QAction( tr( "Use OpenCL" ), this );
  _useOpenCLModeAction-> setCheckable( true  );
  _useOpenCLModeAction-> setChecked  ( false );
  
  connect( _useOpenCLModeAction, SIGNAL( toggled(bool) ), this, SLOT( useOpenCL(bool) ) );
  
  _showFpsAction = new QAction( tr( "Show FPS" ), this );
  _showFpsAction-> setCheckable( true     );
  _showFpsAction-> setChecked  ( _showFPS );
  
   connect( _showFpsAction, SIGNAL( toggled(bool) ), this, SLOT( showFPS(bool) ) );
  
  _showCameraDataAction = new QAction( tr( "Show camera data" ), this );
  _showCameraDataAction-> setCheckable( true            );
  _showCameraDataAction-> setChecked  ( _showCameraData );
  
  connect( _showCameraDataAction, SIGNAL( toggled(bool) ), this, SLOT( showCameraData(bool) ) );
  
  _chooseTextColorAction = new QAction( tr( "Choose text color ..."), this );
  
  connect( _chooseTextColorAction, SIGNAL( triggered() ), this, SLOT( chooseTextColor() ) );
  
}

BaseRenderWidget::~BaseRenderWidget()
{
}

QAction* BaseRenderWidget::useStereoModeAction()
{
  
  return _useStereoModeAction;
  
}

QAction* BaseRenderWidget::useOpenCLModeAction()
{
  
  return _useOpenCLModeAction;
  
}

QAction* BaseRenderWidget::showFpsAction()
{
  
  return _showFpsAction;
  
}

QAction* BaseRenderWidget::showCameraDataAction()
{
  
  return _showCameraDataAction;
  
}

QAction* BaseRenderWidget::chooseTextColorAction()
{
  
  return _chooseTextColorAction;
  
}

void BaseRenderWidget::stopRendering()
{
  
  _timer->stop();
  
}

void BaseRenderWidget::startRendering()
{
  
  _timer->start();
  
}

rtrt::BaseCameraPtr BaseRenderWidget::camera()
{
  return _camera;
}

void BaseRenderWidget::setCamera( rtrt::BaseCameraPtr camera )
{
  _camera = camera;
//   _camera->setFrustum ( );
//   _camera->setViewport ( width(), height() );
}

void BaseRenderWidget::initializeGL()
{
  
  setMinimumSize( DEFAULT_X_SIZE, DEFAULT_Y_SIZE );
  
  GLenum err = glewInit ( );
  if ( GLEW_OK != err )
  {
 //     LOG( ERROR ) << "Error: " << glewGetErrorString ( err ) << "\n";
  }
  LOG( INFO ) << "Status: Using GLEW " << glewGetString ( GLEW_VERSION ) << "\n";
 
  if( renderObject() == NULL )
    qFatal( "Render object not set" );
  
  glMatrixMode ( GL_PROJECTION );
  glLoadIdentity ( );

  glOrtho ( -1.0F, 1.0F, -1.0F, 1.0F, -1.0F, 1.0F );

  glMatrixMode ( GL_MODELVIEW );
  glLoadIdentity ( );

  qglClearColor( Qt::black     );
  glEnable     ( GL_DEPTH_TEST );
  glEnable     ( GL_CULL_FACE  );
  
  renderObject()->initialize( DEFAULT_X_SIZE, DEFAULT_Y_SIZE, true, BaseRender::noStereo );
  
  _fpsTime->start(); 
  _workTime->start(); 
  
  if( renderObject()->drawMode() == BaseRender::drawCL )
    glEnable( GL_TEXTURE_RECTANGLE_ARB );
  
}

void BaseRenderWidget::resizeGL( int width, int height )
{
  glViewport     ( 0, 0, width, height );
  
  renderObject()->resize( width, height );
//   _camera->setViewport( width, height );
}

void BaseRenderWidget::paintGL()
{ 
  
  // glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  draw();
  
}

void BaseRenderWidget::useStereo( bool yes )
{
  
  BaseRender::StereoMode mode;
  
  if( yes )
    mode = BaseRender::softwareStereo;
  else mode = BaseRender::noStereo;
  
  renderObject()->setStereoMode( mode );
  
  _useStereo = yes;
  
}

void BaseRenderWidget::useOpenCL( bool yes )
{
  
  if( yes )
    renderObject()->setDrawMode( BaseRender::drawCL );
  else 
    renderObject()->setDrawMode( BaseRender::drawGL );
  
}

void BaseRenderWidget::updateWindow()
{
  
  updateGL();
  
}

void BaseRenderWidget::showFPS( bool yes )
{
  
  _showFPS = yes;
  
}

void BaseRenderWidget::showCameraData( bool yes )
{
  
  _showCameraData = yes;
  
}

void BaseRenderWidget::chooseTextColor()
{
  
  _text.setColor( QColorDialog::getColor( _text.color(), this ) );
  
}

void BaseRenderWidget::draw()
{
  renderObject()->draw( _camera.get(), static_cast< double >( _workTime->elapsed() ) / 1000 );
  
  if( _showFPS )
    _text.add( "FPS:", _fps );
  if( _showCameraData )
  {
  _text.add( "Camera position: ", _camera->position() ); 
//     _text.add( "Camera orientation: ", _camera->rotation() );
  /*  _text.add( "Camera view    : ", _camera->view    () );
    _text.add( "Camera up      : ", _camera->up      () );
    _text.add( "Camera right   : ", _camera->right   () ); */
  }
  
  _text.draw( this, 10, 15 );
  _text.clear();
  
  swapBuffers();
  nextFrame();
 
}

void BaseRenderWidget::keyPressEvent( QKeyEvent *event )
{
  
  Vector3f delta( 0.0F, 0.0F, 0.0F );
  
  switch( event->key() )
  {
    case Qt::Key_F10:
    {
      useStereo( !_useStereo );
      return;
    }break;
    case Qt::Key_F5:
    {
      useOpenCL( renderObject()->drawMode() != BaseRender::drawCL );
    }break;
    case Qt::Key_W:
    {
      delta[ 1 ] += 0.1;
    }break;
    case Qt::Key_S:
    {
      delta[ 1 ] -= 0.1;
    }break;
    case Qt::Key_A:
    {
      delta[ 0 ] += 0.1;
    }break;
    case Qt::Key_D:
    {
      delta[ 0 ] -= 0.1;
    }break;
    case Qt::Key_Q:
    {
      _camera->rotate( Vector3f( 0.0, 0.0, -5.0 / 360 * M_PI ));
    }break;
    case Qt::Key_E:
    {
      _camera->rotate( Vector3f( 0.0, 0.0, 5.0 / 360 * M_PI ));
    }break;
    case Qt::Key_8:
    {
      GLfloat stereoBase = renderObject()->stereoBase();
      renderObject()->setStereoBase( stereoBase - 0.001 );
    }break;
    case Qt::Key_2:
    {
      GLfloat stereoBase = renderObject()->stereoBase();
      renderObject()->setStereoBase( stereoBase + 0.001 );
    }break;
    default:
    {
      event->ignore();
      return;
    }
  }
  
  _camera->translate( delta );
  
}

void BaseRenderWidget::nextFrame()
{
  
  _frames++;
  int newTime = _fpsTime->elapsed();
  
  if( newTime > _refreshTime )
  {
    
    _fps = static_cast< GLfloat >( _frames ) / newTime * 1000.0;
    _frames = 0;
    _fpsTime->restart();
    
  }
  
}

void BaseRenderWidget::wheelEvent( QWheelEvent *event )
{
  
  _camera->translate( Vector3f( 0.0, 0.0, static_cast< GLfloat >( event->delta() ) / 100 ) );
  
}

void BaseRenderWidget::mousePressEvent( QMouseEvent *event )
{
  
  _lastMousePos = event->pos();
  
}

void BaseRenderWidget::mouseMoveEvent( QMouseEvent *event )
{
  
  GLfloat dx = static_cast< GLfloat >( event->pos().x() - _lastMousePos.x() ) / width () / 2 * M_PI;
  GLfloat dy = static_cast< GLfloat >( event->pos().y() - _lastMousePos.y() ) / height() / 2 * M_PI;
  
  if( ( event->buttons() & Qt::LeftButton ) | ( event->buttons() & Qt::RightButton ) )
    _camera->rotate( Vector3f( dy, -dx, 0.0 ));
  
  _lastMousePos = event->pos();
  
}
